#!/usr/local/bin/python3
# *_* coding: UTF-8 *_*
# @IDE: PyCharm
# @Version: Python3.7
# @Author: Kendrick.Kc
# @Email: 509556985@qq.com
# @File: strategy.py
# @Inst: 这里是模块名描述
# @Time: 2023/7/22 14:55
# -----


from binance_era.middle import Middle
from utils.np import np_round


class ERA(Middle):

    def __init__(self, binance):
        super().__init__(binance)

    def era_run(self):
        # 获取数据并计算信号、保存最新价格、K线结束的时间戳
        self.structural_index()

        # 只平不开状态为False时允许开仓
        if not self.binance.cache_get(self.binance.parameter.db_coll_program):
            # 根据信号判断是否开仓（如果存在持仓则跳过）
            self.calculated_signal()

        # 调整保证金
        self.margin_call()

        # 保存数据
        self.binance.cache_set(self.binance.parameter.db_coll, self.res_db)

        # 判断账户可用余额是否低于警戒值，是则结束程序
        self.account_waring_exit()

        # 只平不开状态判断
        self.program_end()

    # 信号判断
    def calculated_signal(self):
        pos = self.get_pos()

        # 空仓时
        if not pos[0]:
            # 开多
            if self.data["price"] > self.data["shang_gui"] and self.data["rsi"] > self.binance.parameter.set_rsi7:
                self.binance.logger_info(f'准备开[LONG]仓. 现价[{self.data["price"]}], '
                                         f'上轨[{self.data["shang_gui"]}], rsi[{self.data["rsi"]}].')
                self.open_pos("BUY", "LONG")

            # 开空
            if self.data["price"] < self.data["xia_gui"] and self.data["rsi"] < self.binance.parameter.set_rsi2:
                self.binance.logger_info(f'准备开[SHORT]仓. 现价[{self.data["price"]}], '
                                         f'下轨[{self.data["xia_gui"]}], rsi[{self.data["rsi"]}].')
                self.open_pos("SELL", "SHORT")

        # 持仓时
        if pos[0]:
            # 平多
            if self.data["price"] < self.data["xia_gui"] and pos[2] == "LONG":
                self.binance.logger_info(f'准备平[LONG]仓. 现价[{self.data["price"]}], 下轨[{self.data["xia_gui"]}].')
                self.close_ops("SELL", "LONG", pos)

            # 平空
            if self.data["price"] > self.data["shang_gui"] and pos[2] == "SHORT":
                self.binance.logger_info(f'准备平[SHORT]仓. 现价[{self.data["price"]}], 上轨[{self.data["shang_gui"]}].')
                self.close_ops("BUY", "SHORT", pos)

    # 计算开仓金额与数量
    def open_pos(self, side, position_side):
        scale = 0.8
        scale_money = ((1 - self.binance.parameter.set_min_balance) * scale) * 100
        self.binance.logger_info(f"取{np_round((1 - self.binance.parameter.set_min_balance) * 100)}%中{np_round(scale * 100)}%的账户比例, "
                                 f"{np_round(scale_money)} = ((1 - {self.binance.parameter.set_min_balance}) * {scale}) * 100")
        if not self.binance.pan_openings["pan"]:    # 实盘
            trading_money = self.binance.exchange_available_balance()
        else:
            trading_money = self.binance.cache_get(self.binance.parameter.db_coll_up)[-1]
        self.binance.logger_info(f"账户金额[{np_round(trading_money)}], 取[{np_round(scale_money)}]")
        open_money = scale_money * self.binance.parameter.set_leverage
        self.binance.logger_info(f"乘于杠杆, {np_round(open_money)} = {np_round(scale_money)} * {self.binance.parameter.set_leverage}")
        open_quantity = np_round(open_money / self.data["price"], self.binance.parameter.this_decimal)
        self.binance.logger_info(f'开仓数量, {open_quantity} = round({np_round(open_money)} / {self.data["price"]}, {self.binance.parameter.this_decimal})')
        self.res_db[self.binance.parameter.db_coll] = np_round(trading_money - (open_money / self.binance.parameter.set_leverage))
        self.binance.logger_info(f"开[{position_side}]仓后剩余余额. {self.res_db[self.binance.parameter.db_coll]} = {trading_money} - ({np_round(open_money)} / {self.binance.parameter.set_leverage})")
        self.orders(side, position_side, open_quantity)

    # 调整逐仓保证金
    def margin_call(self):
        pos = self.get_pos()

        # 模拟交易暂无爆仓概念
        if self.binance.pan_openings["pan"] or not pos[0]:
            return

        for item in pos[1]:
            number_user = np_round(float(item["isolatedMargin"]) * 1.01, 2)
            if item["positionSide"] == "LONG" and float(item["positionAmt"]) > 0:
                if self.data["xia_gui"] < float(item['liquidationPrice']):
                    self.binance.logger_info(f'调整LONG仓杠杆. 当前[{float(item["liquidationPrice"])}], 追加[{number_user}]')
                    self.binance.change_margin("LONG", number_user)

            if item["positionSide"] == "SHORT" and float(item["positionAmt"]) < 0:
                if self.data["shang_gui"] > float(item['liquidationPrice']):
                    self.binance.logger_info(f'调整SHORT仓杠杆. 当前[{float(item["liquidationPrice"])}], 追加[{number_user}]')
                    self.binance.change_margin("SHORT", number_user)

    # 平仓的方法
    def close_ops(self, side, position_side, pos):
        global money, close_quantity
        if not self.binance.pan_openings["pan"]:    # 实盘
            for item in pos[1]:
                if item["positionSide"] == position_side and abs(float(item["positionAmt"])) > 0:
                    close_quantity = abs(float(item["positionAmt"]))
        else:
            close_quantity = self.res_db[self.binance.parameter.db_coll_pos]
            self.binance.logger_info(f"仓位数量[{close_quantity}]")
            if side == "SELL" and position_side == "LONG":
                money = close_quantity * self.data["price"]
                self.binance.logger_info(f'兑换为金额, {money} = {close_quantity} * {self.data["price"]}')
            if side == "BUY" and position_side == "SHORT":
                price = self.res_db[self.binance.parameter.db_coll_price]
                money = close_quantity * (price + (price - self.data["price"]))
                self.binance.logger_info(f'兑换为金额, {money} = {close_quantity} * ({price} + ({price} - {self.data["price"]}))')
            close_money = money / self.binance.parameter.set_leverage
            self.binance.logger_info(f"除于杠杆得出盈利金额, {close_money} = {money} / {self.binance.parameter.set_leverage}")
            self.res_db[self.binance.parameter.db_coll] = self.res_db[self.binance.parameter.db_coll] + close_money
            self.up_account(np_round(self.res_db[self.binance.parameter.db_coll]))
            self.binance.logger_info(f"历史金额列表: {self.binance.cache_get(self.binance.parameter.db_coll_up)}")

        self.orders(side, position_side, close_quantity)

    # 下单方法
    def orders(self, side, position_side, q):
        res = self.binance.rest("info", "new_order_test" if self.binance.pan_openings["pan"] else "new_order",
                                symbol=self.symbol, side=side, positionSide=position_side, type="MARKET", quantity=q)
        if res:
            if side == "BUY" and position_side == "LONG" or side == "SELL" and position_side == "SHORT":
                self.res_db[self.binance.parameter.db_coll_side] = position_side
                self.res_db[self.binance.parameter.db_coll_pos] = q
                self.res_db[self.binance.parameter.db_coll_price] = self.data["price"]
                self.binance.logger_info(f"本次[开]仓结束. 仓位信息：[{self.res_db}]")
            else:
                self.res_db[self.binance.parameter.db_coll_pos] = 0
                self.binance.logger_info(f"本次[平]仓结束. 仓位信息：[{self.res_db}]")

    # 判断账户可用余额是否低于警戒值，是则结束程序
    def account_waring_exit(self):
        if self.get_pos()[0]:
            return

        if not self.binance.pan_openings["pan"]:
            account = self.binance.exchange_available_balance()
        else:
            account = self.res_db[self.binance.parameter.db_coll]
        init_account = self.binance.cache_get(self.binance.parameter.db_coll_up)[-1]

        if account < (init_account * self.binance.parameter.set_min_balance):
            self.binance.logger_error(f"当前账户可用余额{account}少于上一次结算金额{init_account}的{self.binance.parameter.set_min_balance * 100}%")
            self.is_exit = False

    # 只平状态为True、并且为空仓时，程序结束
    def program_end(self):
        program = self.binance.cache_get(self.binance.parameter.db_coll_program)
        pos = self.get_pos()
        if program and pos[0]:
            self.is_exit = True
            self.binance.logger_info(f"只平不开状态已启用并且仓位为空.")
